1 package org.apache.maven.surefire.junitcore.pc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.surefire.testset.TestSetFailedException;
23 import org.junit.runner.Computer;
24 import org.junit.runner.Description;
25
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.TreeSet;
29 import java.util.concurrent.Callable;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.ScheduledExecutorService;
33
34 import static java.util.concurrent.TimeUnit.*;
35
36
37
38
39
40
41
42
43 public abstract class ParallelComputer
44 extends Computer
45 {
46 private final long timeoutNanos;
47
48 private final long timeoutForcedNanos;
49
50 private ScheduledExecutorService shutdownScheduler;
51
52 private Future<Collection<Description>> testsBeforeShutdown;
53
54 private Future<Collection<Description>> testsBeforeForcedShutdown;
55
56 public ParallelComputer( double timeoutInSeconds, double timeoutForcedInSeconds )
57 {
58 this.timeoutNanos = secondsToNanos( timeoutInSeconds );
59 this.timeoutForcedNanos = secondsToNanos( timeoutForcedInSeconds );
60 }
61
62 private static long secondsToNanos( double seconds )
63 {
64 double nanos = seconds > 0 ? seconds * 1E9 : 0;
65 return Double.isInfinite( nanos ) || nanos >= Long.MAX_VALUE ? 0 : (long) nanos;
66 }
67
68 private static long minTimeout( long timeout1, long timeout2 )
69 {
70 if ( timeout1 == 0 )
71 {
72 return timeout2;
73 }
74 else if ( timeout2 == 0 )
75 {
76 return timeout1;
77 }
78 else
79 {
80 return Math.min( timeout1, timeout2 );
81 }
82 }
83
84 private static Collection<String> printShutdownHook( Future<Collection<Description>> future )
85 throws TestSetFailedException
86 {
87 if ( !future.isCancelled() && future.isDone() )
88 {
89 try
90 {
91 TreeSet<String> executedTests = new TreeSet<String>();
92 for ( Description executedTest : future.get() )
93 {
94 if ( executedTest != null && executedTest.getDisplayName() != null )
95 {
96 executedTests.add( executedTest.getDisplayName() );
97 }
98 }
99 return executedTests;
100 }
101 catch ( Exception e )
102 {
103 throw new TestSetFailedException( e );
104 }
105 }
106 return Collections.emptySet();
107 }
108
109 public abstract Collection<Description> shutdown( boolean shutdownNow );
110
111 protected final void beforeRunQuietly()
112 {
113 testsBeforeShutdown = timeoutNanos > 0 ? scheduleShutdown() : null;
114 testsBeforeForcedShutdown = timeoutForcedNanos > 0 ? scheduleForcedShutdown() : null;
115 }
116
117 protected final void afterRunQuietly()
118 {
119 if ( shutdownScheduler != null )
120 {
121 shutdownScheduler.shutdownNow();
122 }
123 }
124
125 public String describeElapsedTimeout()
126 throws TestSetFailedException
127 {
128 TreeSet<String> executedTests = new TreeSet<String>();
129 if ( testsBeforeShutdown != null )
130 {
131 executedTests.addAll( printShutdownHook( testsBeforeShutdown ) );
132 }
133
134 if ( testsBeforeForcedShutdown != null )
135 {
136 executedTests.addAll( printShutdownHook( testsBeforeForcedShutdown ) );
137 }
138
139 StringBuilder msg = new StringBuilder();
140 if ( !executedTests.isEmpty() )
141 {
142 msg.append( "The test run has finished abruptly after timeout of " );
143 msg.append( nanosToSeconds( minTimeout( timeoutNanos, timeoutForcedNanos ) ) );
144 msg.append( " seconds.\n" );
145 msg.append( "These tests were executed in prior of the shutdown operation:\n" );
146 for ( String executedTest : executedTests )
147 {
148 msg.append( executedTest ).append( '\n' );
149 }
150 }
151 return msg.toString();
152 }
153
154 private Future<Collection<Description>> scheduleShutdown()
155 {
156 return getShutdownScheduler().schedule( createShutdownTask( false ), timeoutNanos, NANOSECONDS );
157 }
158
159 private Future<Collection<Description>> scheduleForcedShutdown()
160 {
161 return getShutdownScheduler().schedule( createShutdownTask( true ), timeoutForcedNanos, NANOSECONDS );
162 }
163
164 private ScheduledExecutorService getShutdownScheduler()
165 {
166 if ( shutdownScheduler == null )
167 {
168 shutdownScheduler = Executors.newScheduledThreadPool( 2 );
169 }
170 return shutdownScheduler;
171 }
172
173 private Callable<Collection<Description>> createShutdownTask( final boolean isForced )
174 {
175 return new Callable<Collection<Description>>()
176 {
177 public Collection<Description> call()
178 throws Exception
179 {
180 return ParallelComputer.this.shutdown( isForced );
181 }
182 };
183 }
184
185 private double nanosToSeconds( long nanos )
186 {
187 return (double) nanos / 1E9;
188 }
189 }